1
|
|
|
/*! |
2
|
|
|
* @package ElkArte Forum |
3
|
|
|
* @copyright ElkArte Forum contributors |
4
|
|
|
* @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file) |
5
|
|
|
* |
6
|
|
|
* @version 2.0 dev |
7
|
|
|
* |
8
|
|
|
* Original code from Aziz, redone and refactored for ElkArte |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
/** global: elk_session_id, elk_session_var, elk_scripturl */ |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* This javascript searches the message for video links and replaces them |
15
|
|
|
* with a clickable preview thumbnail of the video. Once the image is clicked |
16
|
|
|
* the video is embedded in to the page to play. |
17
|
|
|
* |
18
|
|
|
* Currently, works with YouTube, Vimeo, TikTok, Twitter, Facebook, Instagram and DailyMotion |
19
|
|
|
* |
20
|
|
|
*/ |
21
|
|
|
(function($) { |
22
|
|
|
'use strict'; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @param {object} oInstanceSettings holds the text strings to use in the html created |
26
|
|
|
* @param {int} msgid optional to only search for links in a specific id |
27
|
|
|
*/ |
28
|
|
|
$.fn.linkifyvideo = function(oInstanceSettings, msgid) { |
29
|
|
|
let oDefaultsSettings = { |
30
|
|
|
embed_limit: 25, |
31
|
|
|
preview_image: '', |
32
|
|
|
ctp_video: '', |
33
|
|
|
hide_video: '', |
34
|
|
|
youtube: '', |
35
|
|
|
vimeo: '', |
36
|
|
|
dailymotion: '', |
37
|
|
|
tiktok: '', |
38
|
|
|
twitter: '', |
39
|
|
|
facebook: '', |
40
|
|
|
instagram: '' |
41
|
|
|
}; |
42
|
|
|
|
43
|
|
|
// Account for user options |
44
|
|
|
let oSettings = $.extend({}, oDefaultsSettings, oInstanceSettings || {}); |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Replaces the image with the created embed code to show the video |
48
|
|
|
* Called from click event attached to the image |
49
|
|
|
* |
50
|
|
|
* @param {string} tag anchor tag we are replacing with the embed tag |
51
|
|
|
* @param {string} eURL the load or place source link |
52
|
|
|
* @param {boolean} bAspect if to use a tall vs wide |
53
|
|
|
*/ |
54
|
|
|
function showEmbed (tag, eURL, bAspect) |
55
|
|
|
{ |
56
|
|
|
if (bAspect) |
57
|
|
|
{ |
58
|
|
|
$(tag).html(embed_html.replace('{src}', eURL)); |
|
|
|
|
59
|
|
|
} |
60
|
|
|
else |
61
|
|
|
{ |
62
|
|
|
$(tag).html(embed_html_916.replace('{src}', eURL)); |
|
|
|
|
63
|
|
|
} |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Shows the video image and sets up the link |
68
|
|
|
* Sets click event to load video sites embed code |
69
|
|
|
* |
70
|
|
|
* @param {object} a videoID link |
71
|
|
|
* @param {string} src source of image |
72
|
|
|
* @param {string} eURLa play link |
73
|
|
|
* @param {boolean} bAspect false to use a 9/16 iframe vs 16x9 |
74
|
|
|
*/ |
75
|
|
|
function getIMG (a, src, eURLa, bAspect) |
76
|
|
|
{ |
77
|
|
|
return $('' + |
78
|
|
|
'<div class="elk_video">' + |
79
|
|
|
' <a href="' + a.href + '">' + |
80
|
|
|
' <img class="elk_video_preview" alt="' + oSettings.preview_image + '" ' + 'title="' + oSettings.ctp_video + '" src="' + src + '"/>' + |
81
|
|
|
' </a>' + |
82
|
|
|
'</div>') |
83
|
|
|
.on('click', function(e) { |
84
|
|
|
e.preventDefault(); |
85
|
|
|
let tag = this; |
86
|
|
|
showEmbed(tag, eURLa, bAspect); |
87
|
|
|
} |
88
|
|
|
); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Returns a linked preview image. Click on the image to load the player. |
93
|
|
|
* |
94
|
|
|
* @param {string} a link tag of the video |
95
|
|
|
* @param {string} src link of the preview image |
96
|
|
|
* @param {string} eURLa single click event play video |
97
|
|
|
* @param {boolean} bAspect use a wide vs tall ratio |
98
|
|
|
*/ |
99
|
|
|
function embedIMG (a, src, eURLa, bAspect = true) |
100
|
|
|
{ |
101
|
|
|
return getIMG(a, src, eURLa, bAspect); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* Creates and inserts a document fragment. Doing this vs inner/outer HTML ensures that any script |
106
|
|
|
* tags in the embed code will execute. |
107
|
|
|
* |
108
|
|
|
* @param {Element} a the link we are working with |
109
|
|
|
* @param {object} data the data from the ajax call |
110
|
|
|
*/ |
111
|
|
|
function createFragment (a, data) |
112
|
|
|
{ |
113
|
|
|
// Since data.html may contain a script tag that needs to run, we have to add it like this |
114
|
|
|
let parent = a.parentNode, |
115
|
|
|
frag = document.createRange().createContextualFragment('<div class="elk_video">' + data.html + '</div>'); |
116
|
|
|
|
117
|
|
|
parent.parentNode.appendChild(frag); |
118
|
|
|
parent.nextSibling.outerHTML = ''; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Converts a given time value from an array to seconds using a multiplier. |
123
|
|
|
* |
124
|
|
|
* @param {Array} timeArray - The array containing time values. |
125
|
|
|
* @param {number} timeIndex - The index of the time value to convert. |
126
|
|
|
* @param {number} multiplier - The multiplier to apply to the converted time value. |
127
|
|
|
* @return {number} - The converted time value in seconds, or 0 if the time array is undefined or the timeIndex is out of range. |
128
|
|
|
*/ |
129
|
|
|
function convertToSeconds (timeArray, timeIndex, multiplier) |
130
|
|
|
{ |
131
|
|
|
if (typeof timeArray[timeIndex] !== 'undefined') |
132
|
|
|
{ |
133
|
|
|
return parseInt(timeArray[timeIndex]) * multiplier; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
return 0; |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
// The embed code |
140
|
|
|
let domain_regex = /^[^:]*:\/\/(?:www\.)?([^\/]+)(\/.*)$/, |
141
|
|
|
embedded_count = 0, |
142
|
|
|
provider_class = '', |
143
|
|
|
embed_html = '<iframe width="640" height="360" src="{src}" data-autoplay="true" allow="fullscreen" loading="lazy" type="text/html"></iframe>', |
|
|
|
|
144
|
|
|
embed_html_916 = '<iframe width="480" height="800" src="{src}" allow="fullscreen" loading="lazy" type="text/html"></iframe>', |
|
|
|
|
145
|
|
|
handlers = {}, |
146
|
|
|
imgHandlers = {}, |
147
|
|
|
logos = { |
148
|
|
|
tiktok: 'data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 48 48\'%3E%3Cpath fill=\'%23fff\' fill-opacity=\'.01\' d=\'M0 0h48v48H0z\'/%3E%3Cpath fill=\'%232F88FF\' stroke=\'%23000\' stroke-linejoin=\'round\' stroke-width=\'3.833\' d=\'M21.358 19.14c-5.888-.284-9.982 1.815-12.28 6.299-3.446 6.724-.597 17.728 10.901 17.728 11.499 0 11.831-11.111 11.831-12.276V17.876c2.46 1.557 4.533 2.495 6.221 2.813 1.688.317 2.76.458 3.219.422v-6.476c-1.561-.188-2.911-.547-4.05-1.076-1.709-.794-5.096-2.997-5.096-6.226.002.016.002-.817 0-2.499h-7.118c-.021 15.816-.021 24.502 0 26.058.032 2.334-1.779 5.6-5.45 5.6-3.672 0-5.482-3.263-5.482-5.367 0-1.288.442-3.155 2.271-4.538 1.085-.82 2.59-1.147 5.033-1.147V19.14Z\'/%3E%3C/svg%3E', |
149
|
|
|
vimeo: 'data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 455.731 455.731\'%3E%3Cpath fill=\'%231ab7ea\' d=\'M0 0h455.731v455.731H0z\'/%3E%3Cpath fill=\'%23fff\' d=\'m49.642 157.084 17.626 22.474s22.033-17.186 29.965-17.186c4.927 0 15.423 5.729 22.033 25.558 6.61 19.83 34.441 122.62 36.134 127.351 7.607 21.26 17.626 60.811 48.473 66.54s70.065-25.558 91.657-48.473c21.592-22.914 106.64-120.741 110.165-179.349 3.26-54.191-14.517-66.765-22.474-71.828-14.542-9.254-38.778-12.338-61.692-4.407s-57.726 33.931-66.98 80.2c0 0 31.287-11.457 42.744-.441s8.373 35.253-1.322 53.32-37.015 59.93-47.151 61.252c-10.135 1.322-18.067-18.508-19.389-23.796-1.322-5.288-18.067-77.997-24.236-120.3s-33.049-49.354-45.829-49.354c-12.779.001-34.812 9.696-109.724 78.439z\'/%3E%3C/svg%3E', |
150
|
|
|
dailymotion: 'data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 512 512\'%3E%3Cpath fill=\'%230066DC\' fill-rule=\'evenodd\' d=\'M0 512h512V0H0v512Zm441.5-68.635h-76.314v-29.928c-23.443 22.945-47.385 31.424-79.308 31.424-32.421 0-60.354-10.474-83.797-31.424-30.926-27.433-46.887-63.346-46.887-105.245 0-38.407 14.965-72.823 42.896-99.758 24.94-24.44 55.367-36.91 89.284-36.91 32.422 0 57.361 10.973 75.318 33.917V88.724L441.5 72.395v370.97Zm-141.157-202.01c-37.41 0-66.339 30.426-66.339 66.338 0 37.41 28.93 65.841 69.332 65.841 33.918 0 62.349-27.932 62.349-64.843 0-38.406-28.431-67.336-65.342-67.336Z\'/%3E%3C/svg%3E', |
151
|
|
|
twitter: 'data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'126 2 589 589\'%3E%3Ccircle cx=\'420.944\' cy=\'296.781\' r=\'294.5\' fill=\'%232daae1\'/%3E%3Cpath fill=\'%23fff\' d=\'M609.773 179.634c-13.891 6.164-28.811 10.331-44.498 12.204 16.01-9.587 28.275-24.779 34.066-42.86a154.78 154.78 0 0 1-49.209 18.801c-14.125-15.056-34.267-24.456-56.551-24.456-42.773 0-77.462 34.675-77.462 77.473 0 6.064.683 11.98 1.996 17.66-64.389-3.236-121.474-34.079-159.684-80.945-6.672 11.446-10.491 24.754-10.491 38.953 0 26.875 13.679 50.587 34.464 64.477a77.122 77.122 0 0 1-35.097-9.686v.979c0 37.54 26.701 68.842 62.145 75.961-6.511 1.784-13.344 2.716-20.413 2.716-4.998 0-9.847-.473-14.584-1.364 9.859 30.769 38.471 53.166 72.363 53.799-26.515 20.785-59.925 33.175-96.212 33.175-6.25 0-12.427-.373-18.491-1.104 34.291 21.988 75.006 34.824 118.759 34.824 142.496 0 220.428-118.052 220.428-220.428 0-3.361-.074-6.697-.236-10.021a157.855 157.855 0 0 0 38.707-40.158z\'/%3E%3C/svg%3E', |
152
|
|
|
facebook: 'data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 128 128\'%3E%3Cpath fill=\'%233B5998\' d=\'M126 118a8 8 0 0 1-8 8H10a8 8 0 0 1-8-8V10a8 8 0 0 1 8-8h108a8 8 0 0 1 8 8v108z\'/%3E%3Cpath fill=\'%236D84B4\' d=\'M5.667 98.98h116.666v18.039H5.667z\'/%3E%3Cpath fill=\'%23FFF\' d=\'M93.376 117.012H72.203V65.767H61.625v-17.66h10.578V37.504c0-14.407 5.973-22.974 22.943-22.974h14.128v17.662h-8.831c-6.606 0-7.043 2.468-7.043 7.074l-.024 8.839h15.998l-1.872 17.66H93.376v51.247z\'/%3E%3C/svg%3E', |
153
|
|
|
instagram: 'data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 256 256\'%3E%3Cpath fill=\'%230A0A08\' d=\'M128 23.064c34.177 0 38.225.13 51.722.745 12.48.57 19.258 2.655 23.769 4.408 5.974 2.322 10.238 5.096 14.717 9.575 4.48 4.479 7.253 8.743 9.575 14.717 1.753 4.511 3.838 11.289 4.408 23.768.615 13.498.745 17.546.745 51.723 0 34.178-.13 38.226-.745 51.723-.57 12.48-2.655 19.257-4.408 23.768-2.322 5.974-5.096 10.239-9.575 14.718-4.479 4.479-8.743 7.253-14.717 9.574-4.511 1.753-11.289 3.839-23.769 4.408-13.495.616-17.543.746-51.722.746-34.18 0-38.228-.13-51.723-.746-12.48-.57-19.257-2.655-23.768-4.408-5.974-2.321-10.239-5.095-14.718-9.574-4.479-4.48-7.253-8.744-9.574-14.718-1.753-4.51-3.839-11.288-4.408-23.768-.616-13.497-.746-17.545-.746-51.723 0-34.177.13-38.225.746-51.722.57-12.48 2.655-19.258 4.408-23.769 2.321-5.974 5.095-10.238 9.574-14.717 4.48-4.48 8.744-7.253 14.718-9.575 4.51-1.753 11.288-3.838 23.768-4.408 13.497-.615 17.545-.745 51.723-.745M128 0C93.237 0 88.878.147 75.226.77c-13.625.622-22.93 2.786-31.071 5.95-8.418 3.271-15.556 7.648-22.672 14.764C14.367 28.6 9.991 35.738 6.72 44.155 3.555 52.297 1.392 61.602.77 75.226.147 88.878 0 93.237 0 128c0 34.763.147 39.122.77 52.774.622 13.625 2.785 22.93 5.95 31.071 3.27 8.417 7.647 15.556 14.763 22.672 7.116 7.116 14.254 11.492 22.672 14.763 8.142 3.165 17.446 5.328 31.07 5.95 13.653.623 18.012.77 52.775.77s39.122-.147 52.774-.77c13.624-.622 22.929-2.785 31.07-5.95 8.418-3.27 15.556-7.647 22.672-14.763 7.116-7.116 11.493-14.254 14.764-22.672 3.164-8.142 5.328-17.446 5.95-31.07.623-13.653.77-18.012.77-52.775s-.147-39.122-.77-52.774c-.622-13.624-2.786-22.929-5.95-31.07-3.271-8.418-7.648-15.556-14.764-22.672C227.4 14.368 220.262 9.99 211.845 6.72c-8.142-3.164-17.447-5.328-31.071-5.95C167.122.147 162.763 0 128 0Zm0 62.27C91.698 62.27 62.27 91.7 62.27 128c0 36.302 29.428 65.73 65.73 65.73 36.301 0 65.73-29.428 65.73-65.73 0-36.301-29.429-65.73-65.73-65.73Zm0 108.397c-23.564 0-42.667-19.103-42.667-42.667S104.436 85.333 128 85.333s42.667 19.103 42.667 42.667-19.103 42.667-42.667 42.667Zm83.686-110.994c0 8.484-6.876 15.36-15.36 15.36-8.483 0-15.36-6.876-15.36-15.36 0-8.483 6.877-15.36 15.36-15.36 8.484 0 15.36 6.877 15.36 15.36Z\'/%3E%3C/svg%3E', |
154
|
|
|
youtube: 'data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'-100 -100 661 661\'%3E%3Cpath d=\'M365.257 67.393H95.744C42.866 67.393 0 110.259 0 163.137v134.728c0 52.878 42.866 95.744 95.744 95.744h269.513c52.878 0 95.744-42.866 95.744-95.744V163.137c0-52.878-42.866-95.744-95.744-95.744zm-64.751 169.663-126.06 60.123c-3.359 1.602-7.239-.847-7.239-4.568V168.607c0-3.774 3.982-6.22 7.348-4.514l126.06 63.881c3.748 1.899 3.683 7.274-.109 9.082z\' style=\'fill:%23f61c0d\'/%3E%3C/svg%3E', |
155
|
|
|
}; |
156
|
|
|
|
157
|
|
|
// Get a Youtube video thumbnail |
158
|
|
|
imgHandlers.getYoutubeIMG = function(eURL, callback) { |
159
|
|
|
fetchDocument(eURL, ytResponse, 'json'); |
160
|
|
|
|
161
|
|
|
function ytResponse (data) |
162
|
|
|
{ |
163
|
|
|
if (typeof data.thumbnail_url === 'undefined') |
164
|
|
|
{ |
165
|
|
|
callback(logos.youtube); |
166
|
|
|
} |
167
|
|
|
else |
168
|
|
|
{ |
169
|
|
|
callback(data.thumbnail_url); |
170
|
|
|
} |
171
|
|
|
} |
172
|
|
|
}; |
173
|
|
|
|
174
|
|
|
// Get a twitter embed html |
175
|
|
|
imgHandlers.getTwitterEmbed = function(eURL, callback) { |
176
|
|
|
fetchDocument(eURL, twResponse, 'json'); |
177
|
|
|
|
178
|
|
|
function twResponse (data) |
179
|
|
|
{ |
180
|
|
|
if (typeof data.html === 'undefined') |
181
|
|
|
{ |
182
|
|
|
data.html = ''; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
callback(data); |
186
|
|
|
} |
187
|
|
|
}; |
188
|
|
|
|
189
|
|
|
// Get a TikTok video thumbnail and embed data |
190
|
|
|
imgHandlers.getTikTokEmbed = function(eURL, callback) { |
191
|
|
|
fetchDocument(eURL, ttResponse, 'json', false); |
192
|
|
|
|
193
|
|
|
function ttResponse (data) |
194
|
|
|
{ |
195
|
|
|
if (typeof data.html === 'undefined') |
196
|
|
|
{ |
197
|
|
|
data.thumbnail_url = logos.tiktok; |
198
|
|
|
data.html = ''; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
callback(data); |
202
|
|
|
} |
203
|
|
|
}; |
204
|
|
|
|
205
|
|
|
// Get a dailymotion video thumbnail |
206
|
|
|
imgHandlers.getDailymotionIMG = function(eURL, callback) { |
207
|
|
|
fetchDocument(eURL, dailyResponse, 'json', false); |
208
|
|
|
|
209
|
|
|
function dailyResponse (data) |
210
|
|
|
{ |
211
|
|
|
if (typeof data.thumbnail_480_url === 'undefined') |
212
|
|
|
{ |
213
|
|
|
callback(logos.dailymotion); |
214
|
|
|
} |
215
|
|
|
else |
216
|
|
|
{ |
217
|
|
|
callback(data.thumbnail_480_url); |
218
|
|
|
} |
219
|
|
|
} |
220
|
|
|
}; |
221
|
|
|
|
222
|
|
|
// Get a Vimeo video thumbnail |
223
|
|
|
imgHandlers.getVimeoIMG = function(eURL, callback) { |
224
|
|
|
fetchDocument(eURL, vimeoResponse, 'json', false); |
225
|
|
|
|
226
|
|
|
function vimeoResponse (data) |
227
|
|
|
{ |
228
|
|
|
if (typeof data[0].thumbnail_large === 'undefined') |
229
|
|
|
{ |
230
|
|
|
callback(logos.vimeo); |
231
|
|
|
} |
232
|
|
|
else |
233
|
|
|
{ |
234
|
|
|
callback(data[0].thumbnail_large); |
235
|
|
|
} |
236
|
|
|
} |
237
|
|
|
}; |
238
|
|
|
|
239
|
|
|
// Youtube and variants |
240
|
|
|
handlers['youtube.com'] = function(path, a) { |
241
|
|
|
let videoID = path.match(/\bv[=/]([^&#?$]+)/i) || path.match(/#p\/(?:a\/)?[uf]\/\d+\/([^?$]+)/i) || path.match(/(?:\/)([\w-]{11})/i); |
242
|
|
|
|
243
|
|
|
if (!videoID || !(videoID = videoID[1])) |
244
|
|
|
{ |
245
|
|
|
return; |
|
|
|
|
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
// There are two types of YouTube timestamped links |
249
|
|
|
// http://youtu.be/lLOE3fBZcUU?t=1m37s when you click share underneath the video |
250
|
|
|
// http://youtu.be/lLOE3fBZcUU?t=97 when you right click on a video and choose "Copy video URL at current time" |
251
|
|
|
// For embedding, you need to use "?start=97" instead, so we have to convert t=1m37s to seconds while also supporting t=97 |
252
|
|
|
let startAt = path.match(/t=(?:([1-9]{1,2})h)?(?:([1-9]{1,2})m)?(?:([1-9]+)s?)/), |
253
|
|
|
startAtPar = ''; |
254
|
|
|
|
255
|
|
|
if (startAt) |
256
|
|
|
{ |
257
|
|
|
let startAtSeconds = 0; |
258
|
|
|
|
259
|
|
|
startAtSeconds += convertToSeconds(startAt, 1, 3600); // Hours |
260
|
|
|
startAtSeconds += convertToSeconds(startAt, 2, 60); // Minutes |
261
|
|
|
startAtSeconds += convertToSeconds(startAt, 3, 1); // Seconds |
262
|
|
|
|
263
|
|
|
startAtPar = '&start=' + startAtSeconds.toString(); |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
let embedURL = '//www.youtube-nocookie.com/embed/' + videoID + '?rel=0' + startAtPar, |
267
|
|
|
imgURL = '//www.youtube.com/oembed?url=https://www.youtube.com/watch?v=' + videoID + '&format=json', |
268
|
|
|
tag = embedIMG(a, '//i.ytimg.com/vi/' + videoID + '/sddefault.jpg', embedURL + '&autoplay=1'); |
269
|
|
|
|
270
|
|
|
// Get the preview image / embed tag |
271
|
|
|
imgHandlers.getYoutubeIMG(imgURL, function(img) { |
272
|
|
|
$(a).parent().next().find('img').attr('src', img); |
273
|
|
|
}); |
274
|
|
|
|
275
|
|
|
return [oSettings.youtube, tag]; |
276
|
|
|
}; |
277
|
|
|
handlers['m.youtube.com'] = handlers['youtube.com']; |
278
|
|
|
handlers['youtu.be'] = handlers['youtube.com']; |
279
|
|
|
|
280
|
|
|
// Vimeo |
281
|
|
|
handlers['vimeo.com'] = function(path, a) { |
282
|
|
|
let videoID = path.match(/^\/(\d+)/i); |
283
|
|
|
|
284
|
|
|
if (!videoID || !(videoID = videoID[1])) |
285
|
|
|
{ |
286
|
|
|
return; |
|
|
|
|
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
let embedURL = '//player.vimeo.com/video/' + videoID, |
290
|
|
|
imgURL = '//vimeo.com/api/v2/video/' + videoID + '.json', |
291
|
|
|
tag; |
292
|
|
|
|
293
|
|
|
tag = embedIMG(a, logos.vimeo, embedURL + '?autoplay=1'); |
294
|
|
|
|
295
|
|
|
// Get the preview image / embed tag |
296
|
|
|
imgHandlers.getVimeoIMG(imgURL, function(img) { |
297
|
|
|
$(a).parent().next().find('img').attr('src', img); |
298
|
|
|
}); |
299
|
|
|
|
300
|
|
|
return [oSettings.vimeo, tag]; |
301
|
|
|
}; |
302
|
|
|
|
303
|
|
|
// Dailymotion |
304
|
|
|
handlers['dailymotion.com'] = function(path, a) { |
305
|
|
|
let videoID = path.match(/^\/video\/([a-z0-9]{1,18})/i); |
306
|
|
|
|
307
|
|
|
if (!videoID || videoID[1] === '') |
308
|
|
|
{ |
309
|
|
|
return; |
|
|
|
|
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
let embedURL = '//dailymotion.com/embed/video/' + videoID[1], |
313
|
|
|
imgURL = '//api.dailymotion.com/video/' + videoID[1] + '?fields=thumbnail_480_url', |
314
|
|
|
tag; |
315
|
|
|
|
316
|
|
|
tag = embedIMG(a, logos.dailymotion, embedURL + '?related=0&autoplay=1'); |
317
|
|
|
|
318
|
|
|
// Get the preview image or embed tag |
319
|
|
|
imgHandlers.getDailymotionIMG(imgURL, function(img) { |
320
|
|
|
$(a).parent().next().find('img').attr('src', img); |
321
|
|
|
}); |
322
|
|
|
|
323
|
|
|
return [oSettings.dailymotion, tag]; |
324
|
|
|
}; |
325
|
|
|
|
326
|
|
|
// TikTok |
327
|
|
|
handlers['tiktok.com'] = function(path, a) { |
328
|
|
|
let videoID = path.match(/^\/@([0-9A-Za-z_\-.]*)\/video\/([0-9]*)/i); |
329
|
|
|
|
330
|
|
|
if (!videoID) |
331
|
|
|
{ |
332
|
|
|
return; |
|
|
|
|
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
let embedURL = '//www.tiktok.com/oembed?url=https://www.tiktok.com/@' + videoID[1] + '/video/' + videoID[2], |
336
|
|
|
tag; |
337
|
|
|
|
338
|
|
|
imgHandlers.getTikTokEmbed(embedURL, function(data) { |
339
|
|
|
$(a).parent().next().find('img').attr('src', data.thumbnail_url); |
340
|
|
|
a.embedURL = data.html; |
341
|
|
|
}); |
342
|
|
|
|
343
|
|
|
tag = embedIMG(a, logos.tiktok, embedURL); |
344
|
|
|
|
345
|
|
|
// Change the default click event to one that replaces the markup |
346
|
|
|
tag.off('click', '**', false); |
347
|
|
|
tag.on('click', function(e) { |
348
|
|
|
e.preventDefault(); |
349
|
|
|
let load = $(a).parent(); |
350
|
|
|
load.parent().addClass('portrait'); |
351
|
|
|
|
352
|
|
|
load.next().replaceWith('<div class="elk_video">' + a.embedURL + '</div>'); |
353
|
|
|
}); |
354
|
|
|
|
355
|
|
|
return [oSettings.tiktok, tag]; |
356
|
|
|
}; |
357
|
|
|
|
358
|
|
|
// Twitter |
359
|
|
|
handlers['twitter.com'] = function(path, a) { |
360
|
|
|
let videoID = path.match(/\/status\/([0-9]{16,20})/); |
361
|
|
|
|
362
|
|
|
if (!videoID || videoID[1] === '') |
363
|
|
|
{ |
364
|
|
|
return; |
|
|
|
|
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
let embedURL = elk_prepareScriptUrl(elk_scripturl) + 'action=xmlhttp;sa=videoembed;api=json;site=twitter;videoid=' + videoID[1] + ';' + elk_session_var + '=' + elk_session_id, |
368
|
|
|
tag; |
369
|
|
|
|
370
|
|
|
tag = embedIMG(a, logos.twitter, embedURL); |
371
|
|
|
|
372
|
|
|
// Twitter has its own embed codes we need to load, no preview here, replace click event as well |
373
|
|
|
tag.off('click', '**', false); |
374
|
|
|
a.embedURL = embedURL; |
375
|
|
|
a.setAttribute('data-video_embed', 'getTwitterEmbed'); |
376
|
|
|
|
377
|
|
|
return [oSettings.twitter, tag, 'portrait']; |
378
|
|
|
}; |
379
|
|
|
|
380
|
|
|
// Facebook |
381
|
|
|
handlers['facebook.com'] = function(path, a) { |
382
|
|
|
let videoID = path.match(/([\d\w._-]+)?(?:\/videos\/|\/video.php\?v=)(\d+)/i); |
383
|
|
|
|
384
|
|
|
if (!videoID || videoID[1] === '' || videoID[2] === '') |
385
|
|
|
{ |
386
|
|
|
return; |
|
|
|
|
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
let embedURL = '//www.facebook.com/plugins/video.php?href=https://www.facebook.com/' + videoID[1] + '/videos/' + videoID[2], |
390
|
|
|
tag; |
391
|
|
|
|
392
|
|
|
tag = embedIMG(a, logos.facebook, embedURL + '?related=0&autoplay=1'); |
393
|
|
|
|
394
|
|
|
return [oSettings.facebook, tag]; |
395
|
|
|
}; |
396
|
|
|
|
397
|
|
|
// Instagram |
398
|
|
|
handlers['instagram.com'] = function(path, a) { |
399
|
|
|
let videoID = path.match(/\/(?:tv|p)\/([a-z0-9]{10,18})(?:\/\?|\/)?/i); |
400
|
|
|
|
401
|
|
|
if (!videoID || videoID[1] === '') |
402
|
|
|
{ |
403
|
|
|
return; |
|
|
|
|
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
let embedURL = '//www.instagram.com/p/' + videoID[1] + '/embed', |
407
|
|
|
tag; |
408
|
|
|
|
409
|
|
|
tag = embedIMG(a, logos.instagram, embedURL + '?related=0&autoplay=1', false); |
410
|
|
|
|
411
|
|
|
return [oSettings.instagram, tag, 'portrait']; |
412
|
|
|
}; |
413
|
|
|
|
414
|
|
|
// --------------------------------------------------------------------------- |
415
|
|
|
// Get the bbc_link links in the id="msg_1234 divs. |
416
|
|
|
let links; |
417
|
|
|
|
418
|
|
|
if (typeof msgid !== 'undefined') |
419
|
|
|
{ |
420
|
|
|
links = document.querySelectorAll('#' + msgid + ' a.bbc_link'); |
421
|
|
|
} |
422
|
|
|
else |
423
|
|
|
{ |
424
|
|
|
links = document.querySelectorAll('[id^=msg_] a.bbc_link'); |
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
// Create the show/hide button |
428
|
|
|
let showhideBtn = $('' + |
429
|
|
|
'<a class="floatright" title="' + oSettings.hide_video + '">' + |
430
|
|
|
' <i class="icon icon-small i-caret-up" alt=">"></i>' + |
431
|
|
|
'</a>') |
432
|
|
|
.on('click', function() { |
433
|
|
|
let $img = $(this).find('i'), // The open / close icon |
434
|
|
|
$vid = $(this).parent().next(); // The immediate elk_video div |
435
|
|
|
|
436
|
|
|
// Toggle slide the video and change the icon |
437
|
|
|
$img.attr('class', 'icon icon-small ' + ($vid.is(':hidden') !== true ? 'i-caret-down' : 'i-caret-up')); |
438
|
|
|
$vid.slideToggle(); |
439
|
|
|
}); |
440
|
|
|
|
441
|
|
|
// Loop though each link |
442
|
|
|
links.forEach((link) => { |
443
|
|
|
let tag = link, |
444
|
|
|
text = tag.innerText || tag.textContent || ''; |
445
|
|
|
|
446
|
|
|
// Ignore in sentences |
447
|
|
|
if (tag.previousSibling && tag.previousSibling.nodeName === '#text' && tag.previousSibling.nodeValue !== ' ') |
448
|
|
|
{ |
449
|
|
|
return; |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
// Ignore in quotes and signatures |
453
|
|
|
if ('bbc_quote;signature'.indexOf(tag.parentNode.className) !== -1) |
454
|
|
|
{ |
455
|
|
|
return; |
456
|
|
|
} |
457
|
|
|
|
458
|
|
|
// No href or inner text not equal to href attr then we move along |
459
|
|
|
if (tag.href === '' || tag.href.indexOf(text) !== 0) |
460
|
|
|
{ |
461
|
|
|
return; |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
// Get domain and validate we know how to handle it |
465
|
|
|
let m = tag.href.match(domain_regex), |
466
|
|
|
handler = null, |
|
|
|
|
467
|
|
|
args = null; |
|
|
|
|
468
|
|
|
|
469
|
|
|
// One of our video provider domains? |
470
|
|
|
if (embedded_count < oSettings.embed_limit && m !== null && typeof handlers[m[1]] !== 'undefined' && handlers[m[1]] !== null) |
471
|
|
|
{ |
472
|
|
|
// Call the handler and get the tag to insert |
473
|
|
|
handler = handlers[m[1]]; |
474
|
|
|
|
475
|
|
|
// If there are video tags seperated by only a BR node, remove the BR so the video embed can |
476
|
|
|
// be side by side on a wide enough screen. |
477
|
|
|
if (tag.previousSibling && tag.previousSibling.nodeName === 'BR') |
478
|
|
|
{ |
479
|
|
|
if (tag.previousSibling.previousElementSibling && tag.previousSibling.previousElementSibling.classList.contains('elk_video_container')) |
480
|
|
|
{ |
481
|
|
|
tag.previousSibling.remove(); |
482
|
|
|
} |
483
|
|
|
} |
484
|
|
|
|
485
|
|
|
args = handler(m[2], tag, provider_class); |
486
|
|
|
if (args) |
487
|
|
|
{ |
488
|
|
|
embedded_count++; |
489
|
|
|
$(tag).wrap('<div class="elk_video_container ' + (typeof args[2] !== 'undefined' ? args[2] : '') + '">'); |
490
|
|
|
$(tag).wrap('<div class="elk_video_header">').text(args[0]).after(showhideBtn.clone(true)); |
491
|
|
|
$(tag).parent().parent().append(args[1]); |
492
|
|
|
} |
493
|
|
|
} |
494
|
|
|
}); |
495
|
|
|
|
496
|
|
|
// If we have embeded videos, add the lazy load code and events |
497
|
|
|
if (embedded_count > 0) |
498
|
|
|
{ |
499
|
|
|
scrollEmbed(); |
500
|
|
|
} |
501
|
|
|
|
502
|
|
|
/** |
503
|
|
|
* Some sites have no thumbnail, so we mimic an onclick to load the embed when the element is on screen. This |
504
|
|
|
* provides something other than the default logo. |
505
|
|
|
* |
506
|
|
|
* Note: This does now work for all sites, like instagram, due to cors errors. For those you need to set the onclick |
507
|
|
|
* and let the user load the embed. |
508
|
|
|
*/ |
509
|
|
|
function scrollEmbed () |
510
|
|
|
{ |
511
|
|
|
let videoLinks = document.querySelectorAll('a[data-video_embed]'), |
512
|
|
|
throttleTimeout, |
513
|
|
|
found = false; |
514
|
|
|
|
515
|
|
|
/** |
516
|
|
|
* Function that fires to lazy load video sites embed when in viewport |
517
|
|
|
*/ |
518
|
|
|
function videoLinksListener () |
519
|
|
|
{ |
520
|
|
|
if (throttleTimeout) |
521
|
|
|
{ |
522
|
|
|
clearTimeout(throttleTimeout); |
523
|
|
|
} |
524
|
|
|
|
525
|
|
|
// On scroll fires "a lot" so this tames it to be less abusive |
526
|
|
|
throttleTimeout = setTimeout(function() { |
527
|
|
|
videoLinks.forEach(function(a) { |
528
|
|
|
// No links remaining, drop any listeners |
529
|
|
|
if (videoLinks.length === 0) |
530
|
|
|
{ |
531
|
|
|
document.removeEventListener('scroll', videoLinksListener); |
532
|
|
|
window.removeEventListener('resize', videoLinksListener); |
533
|
|
|
window.removeEventListener('orientationChange', videoLinksListener); |
534
|
|
|
} |
535
|
|
|
|
536
|
|
|
// Hey I see you ... |
537
|
|
|
if (isElementInViewport(a)) |
538
|
|
|
{ |
539
|
|
|
let func = a.getAttribute('data-video_embed'); |
540
|
|
|
found = true; |
541
|
|
|
a.removeAttribute('data-video_embed'); |
542
|
|
|
imgHandlers[func](a.embedURL, (data) => { |
543
|
|
|
createFragment(a, data); |
544
|
|
|
}); |
545
|
|
|
} |
546
|
|
|
}); |
547
|
|
|
|
548
|
|
|
// Once a link is found, lets not try that one again. |
549
|
|
|
if (found) |
550
|
|
|
{ |
551
|
|
|
videoLinks = document.querySelectorAll('a[data-video_embed]'); |
552
|
|
|
} |
553
|
|
|
}, 25); |
554
|
|
|
} |
555
|
|
|
|
556
|
|
|
// Scroll, rotate or resize, we check if we can see the video link. |
557
|
|
|
document.addEventListener('scroll', videoLinksListener); |
558
|
|
|
window.addEventListener('DOMContentLoaded', videoLinksListener); |
559
|
|
|
window.addEventListener('resize', videoLinksListener); |
560
|
|
|
window.addEventListener('orientationChange', videoLinksListener); |
561
|
|
|
} |
562
|
|
|
}; |
563
|
|
|
})(jQuery); |
564
|
|
|
|